﻿using Dapper;
using InterpolatedSql.SqlBuilders;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Linq;

namespace InterpolatedSql.Dapper
{
    /// <summary>
    /// Maps from <see cref="InterpolatedSql.SqlParameterInfo" /> to Dapper Parameters.
    /// </summary>
    public class SqlParameterMapper
    {
        /// <summary>
        /// Calculates the name automatically assigned to interpolated parameters
        /// </summary>
        public virtual string CalculateAutoParameterName(InterpolatedSqlParameter parameter, int pos, InterpolatedSqlBuilderOptions options)
        {
            return options.AutoGeneratedParameterPrefix +
                (IsEnumerable(parameter.Argument) ? options.ParameterArrayNameSuffix : "") +
                pos.ToString();
        }

        protected bool IsEnumerable(object? value)
        {
            if (value == null || value is DBNull)  //SqlMapper.GetDbType
                return false;
            Type t = value.GetType();
            return t != typeof(string) && typeof(IEnumerable).IsAssignableFrom(t);
            //TODO: use Dapper SqlMapper.LookupDbType ?
        }

        /// <summary>
        /// Converts from<see cref= "InterpolatedSql.SqlParameterInfo" /> to Dapper Parameters.
        /// </summary>
        public virtual void AddToDynamicParameters(DynamicParameters target, SqlParameterInfo parameter)
        {
            //TODO: do implicit parameters have names here?!
            if (parameter is DbTypeParameterInfo dbParm)
                target.Add(parameter.Name, parameter.Value, dbParm.DbType, parameter.ParameterDirection ?? ParameterDirection.Input, dbParm.Size);
            else if (parameter is StringParameterInfo stringParm)
                target.Add(parameter.Name, new DbString() { Value = (string?)stringParm.Value, IsAnsi = stringParm.IsAnsi, IsFixedLength = stringParm.IsFixedLength, Length = stringParm.Length });
            else if (parameter is SqlParameterInfo parm && parm.Value is IEnumerable<StringParameterInfo> stringParms)
            {
                target.Add(parameter.Name, stringParms.Select(stringParm => new DbString() { Value = (string?)stringParm.Value, IsAnsi = stringParm.IsAnsi, IsFixedLength = stringParm.IsFixedLength, Length = stringParm.Length }));
            }
            else
                target.Add(parameter.Name, parameter.Value);
        }

        /// <summary>
        /// Default mapper. By inheriting/overriding it's possible to modify this behavior
        /// </summary>
        public static SqlParameterMapper Default = new SqlParameterMapper();
    }
}
